<?php //-->
/*
 * This file is part of the Eve package.
 * (c) 2011-2012 Openovate Labs
 *
 * Copyright and license information can be found at LICENSE.txt
 * distributed with this package.
 */

/**
 * Relation Module
 *
 * @package    Eve
 * @category   module
 * @author     Christian Blanquera cblanquera@openovate.com
 */
class Relation extends Eden_Class {
	/* Constants
	-------------------------------*/
	/* Public Properties
	-------------------------------*/
	/* Protected Properties
	-------------------------------*/
	protected $_database 	= NULL;

	/* Private Properties
	-------------------------------*/
	/* Magic
	-------------------------------*/
	public static function i() {
		return self::_getSingleton(__CLASS__);
	}
	
	public function __construct(Eden $app = NULL, $key = NULL) {
		if(is_null($app)) {
			$app = Eden::i()->getActiveApp();
		}
		
		$this->_database 	= $app->database($key);
	}
	
	/* Public Methods
	-------------------------------*/
	/**
	 * Adds a relation
	 *
	 * @param string
	 * @param int
	 * @param string
	 * @param int
	 * @param string|null
	 * @return bool
	 */
	public function add($table1, $id1, $table2, $id2, $type = NULL) {
		Relation_Error::i()
			->argument(1, 'string')
			->argument(2, 'int')
			->argument(3, 'string')
			->argument(4, 'int')
			->argument(5, 'string', 'null');
		
		if($this->related($table1, $id1, $table2, $id2)) {
			return $this;
		}
		
		//add it
		$this->_database->insertRow('relation', array(
			'relation_table1' 	=> $table1,
			'relation_id1' 		=> $id1,
			'relation_table2' 	=> $table2,
			'relation_id2' 		=> $id2,
			'relation_type'		=> $type));
			
		return $this;
	}
	
	/**
	 * Returns a list of relations
	 *
	 * @param string
	 * @param int
	 * @param string
	 * @param string|null|bool
	 * @param bool
	 * @param array
	 * @return array
	 */
	public function getRows($table, $id, $relation, $type = NULL, $join = true, array $sort = array()) {
		Relation_Error::i()
			->argument(1, 'string')
			->argument(2, 'int')
			->argument(3, 'string')
			->argument(4, 'string', 'null', 'bool')
			->argument(5, 'bool');
			
		return $this->search($table, $id, $relation, $type, $join, $sort)->getRows();
	}
	
	/**
	 * installs the relation table
	 *
	 * @return this
	 */
	public function install() {
		$this->_database->query(self::$_schema);
		return $this;
	}
	
	/**
	 * Returns true if related
	 *
	 * @param string
	 * @param int
	 * @param string
	 * @param int
	 * @return bool
	 */
	public function related($table1, $id1, $table2, $id2) {
		Relation_Error::i()
			->argument(1, 'string')
			->argument(2, 'int')
			->argument(3, 'string')
			->argument(4, 'int');
		
		$filter = $join = $sort = array();
		$filter[] = array('(relation_table1=%s AND relation_id1=%s '.
		'AND relation_table2=%s AND relation_id2=%s) OR (relation_table2=%s '.
		'AND relation_id2=%s AND relation_table1=%s AND relation_id1=%s)', 
		$table1, $id1, $table2, $id2, $table1, $id1, $table2, $id2);
		
		$row = $this->_database->getRows('relation', $join, $filter, $sort, 0, 1, 0);
		
		return !empty($row);
	}
	
	/**
	 * Removes a relation
	 *
	 * @param string
	 * @param int
	 * @param string
	 * @param int
	 * @return this
	 */
	public function remove($table1, $id1, $table2, $id2) {
		Relation_Error::i()
			->argument(1, 'string')
			->argument(2, 'int')
			->argument(3, 'string')
			->argument(4, 'int');
			
		if(!$this->related($table1, $id1, $table2, $id2)) {
			return $this;
		}
		
		$filter = array();
		$filter[] = array('(relation_table1=%s AND relation_id1=%s '.
		'AND relation_table2=%s AND relation_id2=%s) OR (relation_table2=%s '.
		'AND relation_id2=%s AND relation_table1=%s AND relation_id1=%s)', 
		$table1, $id1, $table2, $id2, $table1, $id1, $table2, $id2);
		
		$this->_database->deleteRows('relation', $filter);
		
		return $this;
	}
	
	/**
	 * Returns the sql search class to search
	 *
	 * @param string
	 * @param int
	 * @param string
	 * @param string|null|bool
	 * @param bool
	 * @return Eden_Sql_Search
	 */
	public function search($table, $id, $relation, $type = false, $join = true, array $sort = array()) {
		Relation_Error::i()
			->argument(1, 'string')
			->argument(2, 'int')
			->argument(3, 'string')
			->argument(4, 'string', 'null', 'bool')
			->argument(5, 'bool');
		
		$search = $this->_database
			->search()
			->setTable('relation')
			->addFilter('((relation_table1=%s AND relation_id1=%s '.
				'AND relation_table2=%s) OR (relation_table2=%s '.
				'AND relation_id2=%s AND relation_table1=%s))', 
				$table, $id, $relation, $table, $id, $relation);
		
		if($type !== false) {
			$search->filterByRelationType($type);
		}
		
		if($join) {
			$search->setColumns($table.'.*', $relation.'.*')
				->innerJoinOn($table, "(relation_table1='".$table.
				"' AND relation_id1=".$table."_id AND relation_table2='".
				$relation."') OR (relation_table2='".$table.
				"' AND relation_id2=".$table."_id AND relation_table1='".
				$relation."')")
				->innerJoinOn($relation, "(relation_table1='".$relation.
				"' AND relation_id1=".$relation."_id AND relation_table2='".
				$table."') OR (relation_table2='".$relation.
				"' AND relation_id2=".$relation."_id AND relation_table1='".
				$table."')");
		}
		
		foreach($sort as $column => $order) {
			$search->addSort($column, $order);
		}
		
		return $search;
	}
	
	/* Protected Methods
	-------------------------------*/
	/* Private Methods
	-------------------------------*/
	/* Long Data
	-------------------------------*/
	private static $_schema = 
		'CREATE TABLE IF NOT EXISTS `relation` (
		`relation_table1` varchar(255) NOT NULL,
		`relation_id1` int(11) unsigned NOT NULL,
		`relation_table2` varchar(255) NOT NULL,
		`relation_id2` int(11) unsigned NOT NULL,
		`relation_type` varchar(255) DEFAULT NULL,
		KEY `relation_table1` (`relation_table1`),
		KEY `relation_id1` (`relation_id1`),
		KEY `relation_table2` (`relation_table2`),
		KEY `relation_id2` (`relation_id2`),
		KEY `relation_type` (`relation_type`)
		) ENGINE=InnoDB DEFAULT CHARSET=latin1;';
}

/**
 * Relation Error
 */
class Relation_Error extends Eden_Error {
	/* Constants
	-------------------------------*/
	const NO_PRIMARY_SEARCH = 'Relational search requires the user_id set in the model. Try loading the model first.';
	
	/* Public Properties
	-------------------------------*/
	/* Protected Properties
	-------------------------------*/
	/* Private Properties
	-------------------------------*/
	/* Get
	-------------------------------*/
	public static function i($message = NULL, $code = 0) {
		$class = __CLASS__;
		return new $class($message, $code);
	}
	
	/* Magic
	-------------------------------*/
	/* Public Methods
	-------------------------------*/
	/* Protected Methods
	-------------------------------*/
	/* Private Methods
	-------------------------------*/
}